﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Collections;
using System.Data.SqlTypes;
using System.Xml;
using System.Xml.Schema;
using System.Drawing;

namespace Asol.Reporting.Support.XmlPersistor.Internal
{
	/// <summary>
	/// Internal výkonná třída pro XML persistenci. 
	/// Tuto třídu nevyužívat z aplikačního kódu, má se používat třída (Djs.Tools.XmlPersistor.)Persist !!!
	/// </summary>
	internal class XmlPersistor : IDisposable
    {
        #region Konstrukce
        protected XmlPersistor()
        {
            this._TypeLibrary = new TypeLibrary();
        }
        private TypeLibrary _TypeLibrary;
        void IDisposable.Dispose()
        {
            ((IDisposable)this._TypeLibrary).Dispose();
            this._TypeLibrary = null;
        }
		/// <summary>
		/// Označení verze dat při jejich načítání.
		/// Je tak umožněno aktuálním loaderem načíst data uložená v dřívější verzi XmlPersist.
		/// </summary>
		protected string XmlPersistedVersion { get; set; }
        #endregion
        #region Statické internal metody
		/// <summary>
		/// Zajistí persistenci (uložení = serializaci) datového objektu do stringu podle daných parametrů.
		/// Pokud je v parametru uveden soubor (XmlFile), pak XML text uloží do něj (případně vytvoří adresář).
		/// </summary>
		/// <param name="data"></param>
		/// <param name="parameters"></param>
		/// <returns></returns>
        internal static string Serialize(object data, XmlPersistArgs parameters)
		{
            using (XmlPersistor xmlPersist = new XmlPersistor())
            {
                parameters.XmlContent = xmlPersist._Serialize(data, parameters);
            }
			if (parameters.XmlFile != null)
				SaveXmlToFile(parameters.XmlContent, parameters.XmlFile);

			return parameters.XmlContent;
		}
		/// <summary>
		/// Uloží daný XML obsah do daného souboru.
		/// </summary>
		/// <param name="content"></param>
		/// <param name="fileName"></param>
		internal static void SaveXmlToFile(string content, string fileName)
		{
			string eol = Environment.NewLine;
			string text = content.Replace("<?xml version=\"1.0\" encoding=\"utf-16\"?>", "<?xml version=\"1.0\" encoding=\"utf-8\"?>");
			if (SystemIoSupport.CreatePathForFile(fileName, "Adresář pro uložení souboru neexistuje a nelze jej vytvořit."))
			{
				if (System.IO.File.Exists(fileName))
				{
					try
					{
						System.IO.File.Delete(fileName);
					}
					catch (Exception exc)
					{
                        throw new UserException("Zadaný soubor %0 není možno smazat (%1).", fileName, exc.Message);
					}
				}
				if (System.IO.File.Exists(fileName))
				{
                    throw new UserException("Zadaný soubor %0 není možno přepsat.", fileName);
				}
				else
				{
					try
					{
						System.IO.File.WriteAllText(fileName, text, Encoding.UTF8);
					}
					catch (Exception exc)
					{
                        throw new UserException("Do zadaného souboru %0 není možno zapisovat (%1).", fileName, exc.Message);
					}
				}
			}
		}
		/// <summary>
		/// Vytvoří objekt ze serializovaného stavu
		/// </summary>
		/// <param name="parameters"></param>
		/// <returns></returns>
        internal static object Deserialize(XmlPersistArgs parameters)
		{
			using (XmlPersistor xmlPersist = new XmlPersistor())
            {
				return xmlPersist._Deserialize(parameters);
			}
		}
        /// <summary>
        /// Vytvoří objekt ze serializovaného stavu
        /// </summary>
        /// <param name="parameters"></param>
        /// <returns></returns>
        internal static T Deserialize<T>(XmlPersistArgs parameters)
        {
            using (XmlPersistor xmlPersist = new XmlPersistor())
            {
                return xmlPersist._Deserialize<T>(parameters);
            }
        }
        /// <summary>
        /// Určená data rekonstruuje a naplní je do předaného objektu.
        /// Pokud objekt je null, vytvoří jej.
        /// </summary>
        /// <param name="parameters"></param>
        /// <param name="data"></param>
        internal static void LoadTo(XmlPersistArgs parameters, object data)
        {
            using (XmlPersistor xmlPersist = new XmlPersistor())
            {
                object result = xmlPersist._Deserialize(parameters);
                xmlPersist._CloneProperties(result, data);
            }
        }
        #endregion
        #region Privátní výkonné metody: Save
        /// <summary>
        /// 
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private string _Serialize(object data, XmlPersistArgs parameters)
        {
            XmlDocument xDoc = new System.Xml.XmlDocument();
            XmlSchema ts = new XmlSchema();
            ts.TargetNamespace = "http";
            ts.Id = "id";
            xDoc.Schemas.Add(ts);

			XmlElement xRootElement = xDoc.CreateElement("persistent");
			xDoc.AppendChild(xRootElement);
            CreateAttribute("Version", "1.00", xRootElement);
            CreateAttribute("Created", Convertor.DateTimeToString(DateTime.Now), xRootElement);
            CreateAttribute("Creator", System.Windows.Forms.SystemInformation.UserName, xRootElement);

            XmlElement xDataElement = CreateElement("data", xRootElement);
            this.SaveObject(new XmlPersistSaveArgs(data, "Value", null, xDataElement, this._TypeLibrary));

            StringBuilder sb = new StringBuilder();
            XmlWriter xw = XmlWriter.Create(sb, parameters.WriterSettings);
            xDoc.WriteTo(xw);
            xw.Flush();

            return sb.ToString();
        }
        /// <summary>
        /// Uloží předaný objekt do XML.
        /// Tato metoda nezakládá nový element, pokud vstupují data typu Simple.
        /// Ostatní typy dat řeší specializované metody, ty si svoje elementy zakládají.
        /// </summary>
        /// <param name="data">Objekt nesoucí data, reálný</param>
        /// <param name="objectName">Jméno objektu = název property, anebo fixní jméno pokud jde o prvek listu/array/dictionary</param>
        /// <param name="estimatedType">Typ objektu očekávaný (tj. co podle definice má být objekt zač). Může být null.
        /// Pokud se typ reálného objektu liší, bude typ vepsán do atributu.</param>
        /// <param name="typeAttributeName">Název atributu, do kteréhu bude uveden reálný Type datového objektu, pokud se liší od očekávaného (estimatedType).
        /// Pokud nebude určeno, použije se název "Net.Type"</param>
		/// <param name="xElement">Aktuální element, do něhož se budou vkládat hodnoty z objektu</param>
        internal void SaveObject(XmlPersistSaveArgs args)
        {
            // do XML budu persistovat pouze not null hodnoty!
            if (!args.HasData) return;

			// Zjistíme, jaký objekt to sem přišel:
			args.DataTypeInfo = this._TypeLibrary.GetInfo(args.Data.GetType());
			
			// Podle reálného typu, podle jeho charakteru předám řízení do odpovídající metody:
            switch (args.DataTypeInfo.PersistenceType)
            {
                case XmlPersistenceType.Simple:
					this.SimpleTypeSave(args);
                    break;
				case XmlPersistenceType.Self:
					this.SelfTypeSave(args);
					break;
                case XmlPersistenceType.Enum:
					this.EnumTypeSave(args);
                    break;
                case XmlPersistenceType.Array:
                    this.ArrayTypeSave(args);
                    break;
                case XmlPersistenceType.IList:
					this.IListTypeSave(args);
                    break;
                case XmlPersistenceType.IDictionary:
					this.IDictionaryTypeSave(args);
                    break;
                case XmlPersistenceType.Compound:
					this.CompoundTypeSave(args);
                    break;
            }
        }
		#endregion
		#region Privátní výkonné metody: Load
        /// <summary>
		/// Z dodaného XML obsahu sestaví a vrátí odpovídající objekt.
		/// </summary>
		/// <param name="parameters"></param>
		/// <returns></returns>
        private object _Deserialize(XmlPersistArgs parameters)
        {
            return _Deserialize(parameters, null);
        }
        /// <summary>
		/// Z dodaného XML obsahu sestaví a vrátí odpovídající objekt.
		/// </summary>
		/// <param name="parameters"></param>
		/// <returns></returns>
        private T _Deserialize<T>(XmlPersistArgs parameters)
        {
            return (T)_Deserialize(parameters, typeof(T));
        }
		/// <summary>
		/// Z dodaného XML obsahu sestaví a vrátí odpovídající objekt.
		/// </summary>
		/// <param name="parameters"></param>
        /// <param name="estimatedType"></param>
		/// <returns></returns>
		private object _Deserialize(XmlPersistArgs parameters, Type estimatedType)
		{
            parameters.DeserializeStatus = XmlDeserializeStatus.Processing;

			XmDocument xmDoc = null;
			if (!String.IsNullOrEmpty(parameters.XmlContent))
				xmDoc = XmDocument.CreateFromString(parameters.XmlContent);
			else if (!String.IsNullOrEmpty(parameters.XmlFile))
				xmDoc = XmDocument.CreateFromFile(parameters.XmlFile);
            if (xmDoc == null)
            {
                parameters.DeserializeStatus = XmlDeserializeStatus.NotInput;
                return null;
            }

            // Najdu element "persistent", z něj lze načíst číslo verze:
            XmElement xmPers = xmDoc.FindElement("persistent");          // Element "persistent" je Root
            if (xmPers == null)
            {
                parameters.DeserializeStatus = XmlDeserializeStatus.BadFormatPersistent;
                return null;
            }
            this.XmlPersistedVersion = xmPers.FindAttributeValue("Version", "");

            // Najdu element "data", v něm bude uložen objekt:
            XmElement elData = xmDoc.FindElement("persistent", "data");
            if (elData == null)
            {
                parameters.DeserializeStatus = XmlDeserializeStatus.BadFormatData;
                return null;
            }

            // Element Data bude obsahovat buď atribut nebo sub element Value:
            XmAttribute atValue;
            XmElement elValue;
            if (!_FindAttrElementByName(elData, "Value", true, out elValue, out atValue))
            {
                parameters.DeserializeStatus = XmlDeserializeStatus.BadFormatValue;
                return null;
            }

            return _CreateObjectOfType(_CreateLoadArgs(parameters, null, estimatedType, elValue, atValue));
		}
        /// <summary>
        /// Metoda zjistí, zda daný element (inElement) obsahuje atribut daného jména (name): pak ten atribut vloží do out findAttribute, a do out findElement vloží vstupující element a vrátí true.
        /// Pokud nenajde, podívá se do přímo podřízených elementů k elementu inElement, zda v něm najde element daného jména. Pokud najde, pak do out findElement vloží ten nalezený element,
        /// a do findAttribute zkusí najít atribut daného jména v tomto nalezeném elementu.
        /// Pokud nenajde, nechá všude null a vrátí false.
        /// Toto hledání odpovídá stylu ukládání Simple / Compound prvků.
        /// </summary>
        /// <param name="inElement"></param>
        /// <param name="name"></param>
        /// <param name="requiredAttInElement"></param>
        /// <param name="findElement"></param>
        /// <param name="findAttribute"></param>
        /// <returns></returns>
        private bool _FindAttrElementByName(XmElement inElement, string name, bool requiredAttInElement, out XmElement findElement, out XmAttribute findAttribute)
        {
            findElement = null;
            findAttribute = null;

            // Zkusím najít atribut s daným jménem:
            XmAttribute att = inElement.TryFindAttribute(name);
            if (att != null)
            {   // Nalezen atribut (typický vzhled:  <element Value.Type="XmlPersistor.GID" Value="1234;5678" />)
                findElement = inElement;
                findAttribute = att;
                return true;
            }

            // Zkusím hledat element s daným jménem:
            XmElement ele = inElement.TryFindElement(name);
            if (ele != null)
            {   // Uvnitř daného elementu <element> je element <name...> (například: <Value Value.Type="XmlPersistor.DataObject" BindFlags="Public, SetField" ... a další atributy = jednoduché property):
                att = ele.TryFindAttribute(name);
                if (att != null || !requiredAttInElement)
                {   // Nalezen atribut (typický vzhled:  <element Value.Type="XmlPersistor.GID" Value="1234;5678" />)
                    findElement = ele;
                    findAttribute = att;
                    return true;
                }
            }
            return false;
        }
        /// <summary>
        /// Vytvoří argument pro načítání dat.
        /// </summary>
        /// <param name="parameters"></param>
        /// <param name="propInfo"></param>
        /// <param name="estimatedType"></param>
        /// <param name="xmElement"></param>
        /// <param name="xmAttribute"></param>
        /// <returns></returns>
        private XmlPersistLoadArgs _CreateLoadArgs(XmlPersistArgs parameters, PropInfo propInfo, Type estimatedType, XmElement xmElement, XmAttribute xmAttribute)
        {
            Type dataType = estimatedType;
            if (xmAttribute != null && !String.IsNullOrEmpty(xmAttribute.Type))
            {
                Type explicitDataType = TypeLibrary.GetTypeFromSerial(xmAttribute.Type, xmAttribute.Assembly);
                if (explicitDataType != null)
                    dataType = explicitDataType;
            }

            TypeInfo dataTypeInfo = null;
            if (dataType != null)
                dataTypeInfo = this._TypeLibrary.GetInfo(dataType);

            return new XmlPersistLoadArgs(parameters, propInfo, dataType, dataTypeInfo, xmElement, xmAttribute);
        }
        /// <summary>
		/// Vytvoří objekt na základě dat v aktuálním elementu readeru.
		/// Na vstupu je předán reader, defaultní typ prvku, jméno nepovinného atributu, který nese explicitní typ, a jméno atributu který nese hodnotu (platí pouze pro Simple typy).
		/// </summary>
		/// <param name="xmlReader"></param>
		/// <param name="estimatedType"></param>
		/// <param name="typeAttributeName"></param>
		/// <param name="valueAttributeName"></param>
		/// <returns></returns>
		internal object _CreateObjectOfType(XmlPersistLoadArgs args)
		{
			if (args.DataType == null) return null;
            try
            {
                switch (args.DataTypeInfo.PersistenceType)
                {
                    case XmlPersistenceType.Simple:
                        return this.SimpleTypeCreate(args);
                    case XmlPersistenceType.Self:
                        return this.SelfTypeCreate(args);
                    case XmlPersistenceType.Enum:
                        return this.EnumTypeCreate(args);
                    case XmlPersistenceType.Array:
                        break;
                    case XmlPersistenceType.IList:
                        return this.IListTypeCreate(args);
                    case XmlPersistenceType.IDictionary:
                        return null; // this._DictionaryTypeCreate(args);
                    case XmlPersistenceType.Compound:
                        return this.CompoundTypeCreate(args);
                }
            }
            catch (Exception exc)
            {
                string diag = exc.StackTrace;
                throw new DevException("Nelze vytvořit objekt typu %0, chyba: %1.", args.DataType.NsName(), exc.Message);
            }
			return null;
		}
		#endregion
        #region Privátní výkonné metody: CloneProperties
        /// <summary>
        /// Přenese hodnoty (objekty z properties) ze source do target, provede Shallow Copy (MemberwiseClone)
        /// </summary>
        /// <param name="source"></param>
        /// <param name="target"></param>
        private void _CloneProperties(object source, object target)
        {
            // Pokud nejsou vytvořena žádná data, nic neděláme.
            if (source == null || target == null)
                return;

            System.Reflection.PropertyInfo[] propSrcs = source.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            System.Reflection.PropertyInfo[] propTrgs = target.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

            foreach (System.Reflection.PropertyInfo propTrg in propTrgs)
            {   // Pro každou target property:
                // Pokud je property zapisovatelná a klonovatelná:
                if (propTrg.CanWrite && PropInfo.IsPropertyCloneable(propTrg))
                {
                    System.Reflection.PropertyInfo propSrc = propSrcs.FirstOrDefault(p => p.Name == propTrg.Name);         // Najdu property ve zdrojovém objektu?
                    if (propSrc != null)
                    {
                        try
                        {
                            object value = propSrc.GetValue(source, null);
                            propTrg.SetValue(target, value, null);
                        }
                        catch
                        { }
                    }
                }
            }
        }
        #endregion
        #region Simple typy: Save, Create
        /// <summary>
		/// Daný objekt (data) konvertuje pomocí TypeConvertoru pro Simple typ, a výslednou hodnotu vloží do atributu daného jména.
		/// </summary>
		/// <param name="attributeName">Jméno atributu pro uložení hodnoty</param>
		/// <param name="typeInfo">Infromace o typu</param>
		/// <param name="data">Objekt k zápisu. Musí jít o Simple objekt, zápis bude řešit metoda this._TypeLibrary.SimpleTypeSave()</param>
		/// <param name="xDocument"></param>
		/// <param name="xElement"></param>
		private void SimpleTypeSave(XmlPersistSaveArgs args)
		{
			NotifyData(args.Data, XmlPersistState.SaveBegin);

			if (args.DataTypeInfo.TypeConvert == null)
				throw new InvalidOperationException("Nelze serializovat, typ předaný do metody SimpleTypeSave() neobsahuje TypeConvertor.");
			if (args.DataTypeInfo.TypeConvert.Serializator == null)
				throw new InvalidOperationException("Nelze serializovat, typ předaný do metody SimpleTypeSave() obsahuje TypeConvertor, který nemá serializátor.");
			string value = args.DataTypeInfo.TypeConvert.Serializator(args.Data);

			SaveTypeAttribute(args);

			CreateAttribute(args.ObjectName, value, args.XmlElement);

			NotifyData(args.Data, XmlPersistState.SaveDone);
		}
		/// <summary>
		/// Z dodaného readeru načte a sestaví objekt uložený jako Simple (tj. je uložený v jednom atributu jako jeden string)
		/// </summary>
		/// <returns></returns>
		private object SimpleTypeCreate(XmlPersistLoadArgs args)
		{
			if (args.DataTypeInfo.TypeConvert == null)
				throw new InvalidOperationException("Nelze deserializovat, typ předaný do metody SimpleTypeCreate() neobsahuje TypeConvertor.");
			if (args.DataTypeInfo.TypeConvert.Deserializator == null)
				throw new InvalidOperationException("Nelze deserializovat, typ předaný do metody SimpleTypeCreate() obsahuje TypeConvertor, který nemá deserializátor.");

			return args.DataTypeInfo.TypeConvert.Deserializator(args.XmAttribute.ValueFirstOrDefault);
		}
		#endregion
		#region Self typy: Save, Create
		/// <summary>
		/// Uloží do Xml dokumentu daný objekt, který je typu Self (tj. serializaci provádí on sám osobně)
		/// </summary>
		private void SelfTypeSave(XmlPersistSaveArgs args)
		{
			if (args.HasData)
			{
				NotifyData(args.Data, XmlPersistState.SaveBegin);

				if (!(args.Data is IXmlSerializer))
					throw new InvalidOperationException("Nelze serializovat, typ předaný do metody SelfTypeSave() neimplementuje interface IXmlSerializer.");
				string value = ((IXmlSerializer)args.Data).XmlSerialData;

				SaveTypeAttribute(args);

				CreateAttribute(args.ObjectName, value, args.XmlElement);

				NotifyData(args.Data, XmlPersistState.SaveDone);
			}
		}
		/// <summary>
		/// Z dodaného readeru načte a sestaví objekt uložený jako Self (tj. deserializaci provádí on sám osobně)
		/// </summary>
		/// <returns></returns>
		private object SelfTypeCreate(XmlPersistLoadArgs args)
		{
			string xmlData = args.XmAttribute.ValueFirstOrDefault;
			if (xmlData == null)
				return null;

			object data = _ObjectCreate(args.DataType);
			if (data == null)
				return null;
			
			NotifyData(data, XmlPersistState.LoadBegin);
			((IXmlSerializer)data).XmlSerialData = xmlData;
			NotifyData(data, XmlPersistState.LoadDone);

			return data;
		}
		#endregion
		#region Enum typy: Save, Create
		/// <summary>
		/// Uloží do Xml dokumentu daný objekt, který je typu Self (tj. serializaci provádí on sám osobně)
		/// </summary>
		/// <param name="attributeName">Jméno atributu pro uložení hodnoty</param>
		/// <param name="typeInfo">Infromace o typu</param>
		/// <param name="data">Objekt k zápisu. Musí jít o Enum objekt</param>
		/// <param name="xDocument"></param>
		/// <param name="xElement"></param>
		private void EnumTypeSave(XmlPersistSaveArgs args)
        {
			string value = Enum.Format(args.DataType, args.Data, "F");

			SaveTypeAttribute(args);

            CreateAttribute(args.ObjectName, value, args.XmlElement);
        }
		/// <summary>
		/// Z dodaného readeru načte a sestaví objekt uložený jako Enum
		/// </summary>
		/// <param name="xmlReader"></param>
		/// <param name="type"></param>
		/// <param name="valueAttributeName"></param>
		/// <returns></returns>
		private object EnumTypeCreate(XmlPersistLoadArgs args)
		{
			string xmlData = args.XmAttribute.ValueFirstOrDefault;
			if (xmlData == null)
				return null;

			object data = null;
			try
			{
				data = Enum.Parse(args.DataType, xmlData);
			}
			catch
			{
				data = _ObjectCreate(args.DataType);                  // Vytvoří objekt, prázdný (hodnota = 0)
			}
			return data;
		}
		#endregion
        #region Array typy: Save, Create
        /// <summary>
        /// Uloží do Xml dokumentu daný objekt, který je typu Array
        /// </summary>
        /// <param name="attributeName">Jméno atributu pro uložení hodnoty</param>
        /// <param name="typeInfo">Infromace o typu</param>
        /// <param name="data">Objekt k zápisu. Musí jít o Enum objekt</param>
        /// <param name="xDocument"></param>
        /// <param name="xElement"></param>
        private void ArrayTypeSave(XmlPersistSaveArgs args)
        {
            // Zavedu element za celý List,například <Array ...>...</Array>:
            XmlElement xmlArrayElement = CreateElement(args.ObjectName, args.XmlElement);

            // Pokud se skutečný Type objektu s daty liší od očekávaného typu, vepíšu jeho Type jako atribut:
            SaveTypeAttribute(args, xmlArrayElement);
            Array a = args.Data as Array;
            Type itemType = args.DataTypeInfo.ItemDataType;

            // Rozměry pole:  [0÷2,0÷45] :
            string range = "";
            for (int r = 0; r < a.Rank; r++)
                range += (range.Length == 0 ? "" : ",") + a.GetLowerBound(r).ToString() + "+" + a.GetLength(r).ToString();
                // range += (range.Length == 0 ? "" : ",") + a.GetLowerBound(r).ToString() + "÷" + a.GetUpperBound(r).ToString();
            range = "[" + range + "]";
            CreateAttribute("Array.Range", range, xmlArrayElement);

            // Low a High Bounds:
            int[] indicesLow = new int[a.Rank];
            int[] indicesHigh = new int[a.Rank];
            for (int r = 0; r < a.Rank; r++)
            {
                indicesLow[r] = a.GetLowerBound(r);
                indicesHigh[r] = a.GetUpperBound(r);
            }

            // Výpis všech Items:
            int[] indices = new int[a.Rank];               // Aktuální mapa dimenzí (průběžně se bude měnit)
            indicesLow.CopyTo(indices, 0);
            string itemName = args.GetItemName("Item");
            while (true)
            {
                // Načtu hodnotu prvku adresovaného pointerem indices, a vepíšu jej do výstupu:
                bool valid = true;
                for (int r = 0; r < a.Rank; r++)
                {
                    if (indices[r] < 0 || indices[r] > indicesHigh[r])
                    {
                        valid = false;
                        break;
                    }
                }
                if (valid)
                {
                    object item = a.GetValue(indices);
                    XmlElement xmlItemElement = CreateElement(itemName, xmlArrayElement);
                    // Vypíšu indices:
                    string ptr = "";
                    for (int r = 0; r < a.Rank; r++)
                        ptr += (ptr.Length == 0 ? "" : ",") + indices[r].ToString();
                    CreateAttribute(itemName + ".Indices", "[" + ptr + "]", xmlItemElement);

                    // Vypíšu obsah itemu:
                    if (item != null)
                        this.SaveObject(new XmlPersistSaveArgs(item, "Value", itemType, xmlItemElement, this._TypeLibrary));
                }

                // Pokročím na další indices nebo skončím:
                bool end = true;
                for (int r = a.Rank - 1; r >= 0; r--)     // Dimenze: 2,1,0 (pořadí indexu v adresaci prvku pole N-té dimenze)
                {
                    indices[r]++;                         // Postupně zvyšuji index v dané dimenzi (zprava): [0,0,0], [0,0,1], [0,0,2], [0,1,0], [0,1,1], ...
                    if (indices[r] <= indicesHigh[r])     // Pokud tato dimenze není dokončena, vyběhnu s příznakem !end:
                    {
                        end = false;
                        break;
                    }
                    indices[r] = indicesLow[r];           // Tuto dimenzi nastavím na Low, a buď jdu dál (zvýším další dimenzi) nebo skončím (vše je hotovo).
                }
                if (end) break;
            }
        }
        /// <summary>
        /// Z dodaného readeru načte a sestaví objekt uložený jako Array
        /// </summary>
        /// <param name="xmlReader"></param>
        /// <param name="type"></param>
        /// <param name="valueAttributeName"></param>
        /// <returns></returns>
        private object ArrayTypeCreate(XmlPersistLoadArgs args)
        {
            return null;
        }
        #endregion
        #region IList typy: Save, Create
		/// <summary>
		/// Uloží kolekci (List)
		/// </summary>
		/// <param name="iCollection"></param>
		/// <param name="elementName"></param>
		/// <param name="xDocument"></param>
		/// <param name="xParentElement"></param>
		internal void IListTypeSave(XmlPersistSaveArgs args)
        {
            // Zavedu element za celý List,například <ItemList ...>...</ItemList>:
			XmlElement xmlListElement = CreateElement(args.ObjectName, args.XmlElement);

			// Pokud se skutečný Type objektu s daty liší od očekávaného typu, vepíšu jeho Type jako atribut:
			SaveTypeAttribute(args, xmlListElement);

			IList iList = args.Data as IList;
			if (iList == null) return;

			Type itemType = args.DataTypeInfo.GetGenericType(0);

			string itemName = args.GetItemName("Item");
			foreach (object item in iList)
			{
				// Element za tento prvek seznamu založíme, protože je třeba zachovávat pořadí položek bez vynechávání null položek:
                XmlElement xmlItemElement = CreateElement(itemName, xmlListElement);
				if (args.HasData)
					this.SaveObject(new XmlPersistSaveArgs(item, "Value", itemType, xmlItemElement, this._TypeLibrary));
			}
		}
        internal object IListTypeCreate(XmlPersistLoadArgs args)
        {
            // Vytvořím objekt s daty odpovídajícími datům persistovaným:
            object data = _ObjectCreate(args.DataType);
            IList iList = data as IList;
            if (iList == null) return null;

            NotifyData(data, XmlPersistState.LoadBegin);

            args.DataTypeInfo = this._TypeLibrary.GetInfo(data.GetType());           // Reálný Type + jeho property
            Type itemType = args.DataTypeInfo.ItemDataType;
            if (itemType == null)
                itemType = args.DataTypeInfo.GetGenericType(0);

            string itemName = (args.PropInfo != null && !String.IsNullOrEmpty(args.PropInfo.XmlItemName) ? args.PropInfo.XmlItemName : "Item");

            foreach (XmElement xmEle in args.XmElement.XmElements)
            {
                if (xmEle.Name == itemName)
                {   // Element má odpovídající jméno. 
                    object value = null;
                    // Buď obsahuje atribut, nebo podřízený element s názvem Value:
                    XmAttribute atValue;
                    XmElement elValue;
                    if (_FindAttrElementByName(xmEle, "Value", false, out elValue, out atValue))
                    {
                        XmlPersistLoadArgs itemArgs = this._CreateLoadArgs(args.Parameters, null, itemType, elValue, atValue);
                        value = this._CreateObjectOfType(itemArgs);
                    }
                    iList.Add(value);
                }
            }

            NotifyData(data, XmlPersistState.LoadDone);

            return data;
        }
		#endregion
		#region IDictionary typy: Save, Create
		/// <summary>
		/// Uloží Dictionary
		/// </summary>
		internal void IDictionaryTypeSave(XmlPersistSaveArgs args)
		{
            // string currentName, string itemName, TypeInfo typeInfo, object data, Type estimatedType, string typeAttributeName, XmlElement xElement)
			XmlElement xmlDictElement = CreateElement(args.ObjectName, args.XmlElement);  // Zavedu element za celý List,například <ItemList ...>...</ItemList>

			// Pokud se skutečný Type objektu s daty liší od očekávaného typu, vepíšu jeho Type jako atribut:
			SaveTypeAttribute(args, xmlDictElement);


			IDictionary iDict = args.Data as IDictionary;
			if (iDict == null) return;

			Type keyType = args.DataTypeInfo.GetGenericType(0);
			Type valueType = args.DataTypeInfo.GetGenericType(1);

			string itemName = args.GetItemName("Entry");
			foreach (DictionaryEntry entry in iDict)
			{
				// Element za tento prvek seznamu založíme, protože je třeba zachovávat pořadí položek bez vynechávání null položek:
                XmlElement xmlItemElement = CreateElement(itemName, xmlDictElement);

				this.SaveObject(new XmlPersistSaveArgs(entry.Key, "Key", keyType, xmlItemElement, this._TypeLibrary));
				this.SaveObject(new XmlPersistSaveArgs(entry.Value, "Value", valueType, xmlItemElement, this._TypeLibrary));
			}
		}
		#endregion
		#region Compound typy: Save, Create
		/// <summary>
		/// Uloží do Xml dokumentu daný objekt, který je typu Compound (tj. má property)
		/// </summary>
		/// <param name="currentName">Jméno atributu pro uložení hodnoty</param>
		/// <param name="typeInfo">Infromace o typu</param>
		/// <param name="data">Objekt k zápisu. Musí jít o Enum objekt</param>
		/// <param name="xDocument"></param>
		/// <param name="xElement"></param>
		internal void CompoundTypeSave(XmlPersistSaveArgs args)
		{
			NotifyData(args.Data, XmlPersistState.SaveBegin);

			XmlElement xmlCurrElement = CreateElement(args.ObjectName, args.XmlElement);
			foreach (PropInfo propInfo in args.DataTypeInfo.PropertyList)
			{
				object value = propInfo.Property.GetValue(args.Data, null);
				this.SaveObject(new XmlPersistSaveArgs(value, propInfo.XmlName, propInfo.PropertyType, propInfo.XmlItemName, xmlCurrElement, this._TypeLibrary));
			}
            SaveTypeAttribute(args, xmlCurrElement);

			NotifyData(args.Data, XmlPersistState.SaveDone);
		}
        /// <summary>
        /// Vytvoří a vrátí objekt typu Compound z dat XML
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
		internal object CompoundTypeCreate(XmlPersistLoadArgs args)
		{
            // Vytvořím objekt s daty odpovídajícími datům persistovaným:
			object data = _ObjectCreate(args.DataType);
			NotifyData(data, XmlPersistState.LoadBegin);
            args.DataTypeInfo = this._TypeLibrary.GetInfo(data.GetType());             // Reálný Type + jeho property

            // 1. Projdeme atributy, ty obsahují jednoduché datové typy. Uložíme je do property našeho objektu:
            foreach (XmAttribute xmAtt in args.XmElement.XmAttributes)
            {
                PropInfo propInfo = args.DataTypeInfo.FindPropertyByXmlName(xmAtt.Name);
                if (propInfo != null)
                {
                    XmlPersistLoadArgs propArgs = this._CreateLoadArgs(args.Parameters, propInfo, propInfo.PropertyType, args.XmElement, xmAtt);
                    object value = this._CreateObjectOfType(propArgs);
                    propInfo.Property.SetValue(data, value, null);
                }
            }

            // 2. Projdeme si sub-elementy našeho elementu. pro každý z nich určím zda máme cílovou property, a pak načtu hodnotu:
            foreach (XmElement xmEle in args.XmElement.XmElements)
            {
                PropInfo propInfo = args.DataTypeInfo.FindPropertyByXmlName(xmEle.Name);
                if (propInfo != null)
                {
                    XmAttribute xmAte = xmEle.TryFindAttribute(xmEle.Name);            // Pokud je v elementu uložen obraz objektu, který je jiného typu než je očekáván v property, pak je zde uložen i konkrétní Type
                    XmlPersistLoadArgs propArgs = this._CreateLoadArgs(args.Parameters, propInfo, propInfo.PropertyType, xmEle, xmAte);
                    object value = this._CreateObjectOfType(propArgs);
                    propInfo.Property.SetValue(data, value, null);
                }
            }

			NotifyData(data, XmlPersistState.LoadDone);
			return data;
		}
		#endregion
		#region Xml prvky - tvorba, zápisy, nalezení
		/// <summary>
		/// Založí nový element do elementu předaného. Nic do něj nevpisuje. Vrátí tento nově založený element.
		/// </summary>
		/// <param name="elementName">Název elementu (typicky jde o název property, nebo klíčové slovo pro element za položku listu, atd)</param>
		/// <param name="xParentElement"></param>
		/// <returns></returns>
        internal static XmlElement CreateElement(string elementName, XmlElement xParentElement)
        {
            string name = CreateXmlName(elementName);
			XmlElement xElement = xParentElement.OwnerDocument.CreateElement(name);
            xParentElement.AppendChild(xElement);
			return xElement;
        }
		/// <summary>
		/// Z daného textu vyloučí nevhodné znaky a výsledek vrátí. Nemění ToLower.
		/// Odstraňuje ampersand, čárku, mezeru, hranaté závorky.
		/// </summary>
		/// <param name="name"></param>
		/// <returns></returns>
		internal static string CreateXmlName(string name)
		{
			string excludeChars = "`, []";
			string xmlName = name;
			foreach (char c in excludeChars)
			{
				if (xmlName.Contains(c))
					xmlName = xmlName.Replace(c.ToString(), "");
			}
			return xmlName;
		}
		/// <summary>
		/// Založí nový atribut a vloží jej do předaného elementu.
		/// </summary>
		/// <param name="name">Název atributu</param>
		/// <param name="value">Hodnota do atributu, string. Pokud bude null, bude atribut bez hodnoty.</param>
		/// <param name="xElement"></param>
        internal static XmlAttribute CreateAttribute(string name, string value, XmlElement xElement)
		{
			XmlAttribute xAttribute = xElement.OwnerDocument.CreateAttribute(name);
			if (value != null)
				xAttribute.Value = value;
			xElement.Attributes.Append(xAttribute);
			return xAttribute;
		}
		/// <summary>
		/// V případě potřeby uloží do aktuálního elementu atribut, který ponese aktuální Type.
		/// Případ potřeby je tehdy, když se aktuální Type (currentType) liší od očekávaného (estimatedType).
		/// </summary>
        internal static void SaveTypeAttribute(XmlPersistSaveArgs args)
		{
			SaveTypeAttribute(args, args.XmlElement);
		}
		/// <summary>
		/// V případě potřeby uloží do aktuálního elementu atribut, který ponese aktuální Type.
		/// Případ potřeby je tehdy, když se aktuální Type (currentType) liší od očekávaného (estimatedType).
		/// </summary>
        internal static void SaveTypeAttribute(XmlPersistSaveArgs args, XmlElement xmlElement)
		{
            // Neliší se typ reálný a očekávaný? OK, žádný atribut s typem se neuloží.
            if (Type.Equals(args.DataType, args.EstimatedType)) return;

            // Získám serializovatelné hodnoty o typu:
            string serialType, serialAssembly;
            TypeLibrary.GetSerialForType(args.DataType, out serialType, out serialAssembly);

            // Vytvořím atributy pro neprázdné serial hodnoty:
            if (!String.IsNullOrEmpty(serialType))
                CreateAttribute(args.ObjectName + ".Type", serialType, xmlElement);
            if (!String.IsNullOrEmpty(serialAssembly))
                CreateAttribute(args.ObjectName + ".Assembly", serialAssembly, xmlElement);
		}
        /// <summary>
        /// Najde v daném readeru začátek daného elementu, 
        /// </summary>
        /// <param name="elementName"></param>
        internal static bool XmlReadFindElement(XmlTextReader xmlReader, string elementName, XmlElementDepthType depthType)
        {
            int depth = xmlReader.Depth;
            while (!xmlReader.EOF)
            {
                if (xmlReader.NodeType == XmlNodeType.Element && xmlReader.Name == elementName)
                {
                    switch (depthType)
                    {
                        case XmlElementDepthType.None:
                            break;
                        case XmlElementDepthType.Anywhere:
                            return true;
                        case XmlElementDepthType.InCurrentDepth:
                            if (xmlReader.Depth == depth)
                                return true;
                            break;
                        case XmlElementDepthType.CurrentAndAnyChilds:
                            if (xmlReader.Depth >= depth)
                                return true;
                            break;
                        case XmlElementDepthType.OnlyInChilds:
                            if (xmlReader.Depth > depth)
                                return true;
                            break;
                    }
                    return true;
                }
                xmlReader.Read();
            }
            return false;
        }
		#endregion
		#region Privátní výkonné metody: obecné
		/// <summary>
		/// Vytvoří a vrátí objekt daného typu
		/// </summary>
		/// <param name="type"></param>
		/// <returns></returns>
		private object _ObjectCreate(Type type)
		{
			object result = null;
			ConstructorInfo constructor = this.CheckConstructors(type);     // Ověří, zda Type má bezparametrický konstruktor. Vrátí jej.
			if (constructor != null)
				result = constructor.Invoke(null);
			else
				// Například struktury nemají bezparametrický konstruktor definovaný, proto vrací null. Přesto je lze standardně vytvořit:
				result = System.Activator.CreateInstance(type);
			return result;
		}
		/// <summary>
		/// Metoda ověří, zda typ má bezparametrický konstruktor.
		/// Pokud jej nemá, vyhodí chybu.
		/// Pokud jej má, vrátí jej.
		/// </summary>
		/// <param name="type"></param>
		/// <returns></returns>
		internal ConstructorInfo CheckConstructors(Type type)
		{
			if (type.IsClass)
			{
				ConstructorInfo[] typeConsts = type.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);   // Najdu konstruktory daného Type
				ConstructorInfo[] typeConstNps = typeConsts.Where(c => c.GetParameters().Length == 0).ToArray();     // Najdu jen ty bezparametrické...
				if (typeConstNps.Length == 0)
					throw new InvalidOperationException("Type " + type.Namespace + "." + type.Name + " can not be persisted, must be a type with parameterless constructor!");
				return typeConstNps[0];
			}
			if (type.IsInterface)
			{
				throw new InvalidOperationException("Type " + type.Namespace + "." + type.Name + " is interface. Object can not be created.!");
			}
			if (type.IsValueType || type.IsEnum || type.IsPrimitive)
			{
				return null;
			}
			throw new InvalidOperationException("Type " + type.Namespace + "." + type.Name + " is unknown type. Object can not be created.!");
		}
		/// <summary>
		/// Oznámí datovému objektu změnu stavu na danou hodnotu.
		/// Datový objekt se tak může připravit na nadcházející události (vkládání dat do property v režimu Load() nemusí vyvolávat množství logiky, a nemělo by vyvolávat mnoho chyb).
		/// Ukončení tohoto stavu (Load / Save) bude oznámeno vložením hodnoty LoadDone nebo SaveDone, a pak ihned None.
		/// Pokud daný objekt nepodporuje IXmlPersistNotify, pak tato metoda nic nevkládá.
		/// </summary>
		/// <param name="xmlPersistState"></param>
		internal static void NotifyData(object data , XmlPersistState xmlPersistState)
		{
            if (data != null && data is IXmlPersistNotify)
            {
                IXmlPersistNotify xmlPers = data as IXmlPersistNotify;
                xmlPers.XmlPersistState = xmlPersistState;

                // Ze stavu LoadDone a SaveDone automaticky přejdu do stavu None:
                if (xmlPersistState == XmlPersistState.LoadDone || xmlPersistState == XmlPersistState.SaveDone)
                    xmlPers.XmlPersistState = XmlPersistState.None;
            }
		}
        #endregion
        #region Ukázky XML
        /*    XML sample

	Příklad persistence objektu s následující strukturou:
class Sample
{
	int Id { get; private set; }                              // Jednoduchá property
	string Name { get; set; }                                 // Jednoduchá property
	List<string> Lines { get; set; }                          // Seznam s položkami, jejichž typ jednoduchý
	List<SampleItem> Items { get; private set; }              // Seznam s položkami, jejichž typ je složený
	Dictionary<int, SampleItem> Cached { get; private set; }  // Dictionary s položkami
	bool HasItems { get { return (this.Items.Count > 0); }    // Nemá set = nebude se ukládat
}
class SampleItem
{
	int Id { get; private set; }                              // Jednoduchá property
	string Name { get; set; }                                 // Jednoduchá property
}

XML reprezentace:
<?xml version="1.0" encoding="utf-16"?>
<persistent Version="1.00">
    <data>
        <Value Value.Type="XmlPersistor.DataObject" BindFlags="Default" Count="0" ObjectId="0" InternalDecimal="0.00" NextGId="0;0" Operation="0" PrevGId="0;0" Product_Order_Structure="" Product_Order26="" VstupníStrana="Vychod">
            <Days>
                <Day Value="1" />
                <Day Value="2" />
                <Day Value="3" />
            </Days>
            <ItemDict />
            <ItemList />
            <MatrixFloat Array.Range="[0+1,0+1]">
                <Item Item.Indices="[0,0]" Value="0.00" />
            </MatrixFloat>
            <MatrixObj Array.Range="[0+0,0+0]" />
            <QItem0 Id="0" />
        </Value>
    </data>
</persistent>


anebo neprázdný objekt:
<?xml version="1.0" encoding="utf-16"?>
<persistent Version="1.00">
    <data>
        <Value Value.Type="XmlPersistor.DataObject" BindFlags="Public, SetField" ComparableKey.Type="System.Int32" ComparableKey="654" Count="0" Description="Zde je &lt;menší&gt; nebo větší == objekt, % s blbými znaky &lt;!-- ! --&gt;" ObjectId="0" InternalDecimal="0.00" NextGId="25;2100" Operation="0" PrevGId="25;1655" Product_Order_Structure="25614" Product_Order26="126" VstupníStrana="16">
            <Auditor Auditor.Type="System.Collections.Generic.List`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]">
                <Item Value="Informace" />
                <Item Value="Varování" />
                <Item Value="Chyba" />
            </Auditor>
            <Days>
                <Day Value="1" />
                <Day Value="2" />
                <Day Value="3" />
                <Day Value="123" />
                <Day Value="234" />
                <Day Value="345" />
                <Day Value="321654" />
                <Day Value="2541" />
                <Day Value="3321" />
                <Day Value="52" />
                <Day Value="12385" />
                <Day Value="3366" />
                <Day Value="3368" />
                <Day Value="3658" />
            </Days>
            <Item1 Id="0" Key="It1" Text="Položka 1A" />
            <Item3 Id="0" Key="It3" Text="Položka 3C" />
            <ItemDict>
                <Item Key="1">
                    <Value Id="0" Text="ukázka1" />
                </Item>
            </ItemDict>
            <ItemList>
                <Item>
                    <Value Id="0" Text="text1" />
                </Item>
            </ItemList>
            <MatrixFloat Array.Range="[0+2,0+3]">
                <Item Item.Indices="[0,0]" Value="1.00" />
                <Item Item.Indices="[0,1]" Value="2.00" />
                <Item Item.Indices="[0,2]" Value="3.00" />
                <Item Item.Indices="[1,0]" Value="10.00" />
                <Item Item.Indices="[1,1]" Value="11.00" />
                <Item Item.Indices="[1,2]" Value="12.00" />
            </MatrixFloat>
            <MatrixObj Array.Range="[0+2,0+4]">
                <Item Item.Indices="[0,0]" Value.Type="System.Single" Value="0.01" />
                <Item Item.Indices="[0,1]" Value.Type="System.Single" Value="0.02" />
                <Item Item.Indices="[0,2]" Value.Type="System.Single" Value="0.03" />
                <Item Item.Indices="[0,3]" Value.Type="System.Single" Value="0.04" />
                <Item Item.Indices="[1,0]" Value.Type="System.Single" Value="1.01" />
                <Item Item.Indices="[1,1]" Value.Type="System.Single" Value="1.02" />
                <Item Item.Indices="[1,2]" Value.Type="System.Single" Value="1.03" />
                <Item Item.Indices="[1,3]" Value.Type="System.Single" Value="1.04" />
            </MatrixObj>
            <Object3 Object3.Type="XmlPersistor.Item" Id="0" Key="ItObj3" Text="Položka jakoby objekt" />
            <QItem0 Id="0" Key="Klíč" Text="Název" />
        </Value>
    </data>
</persistent>
    
    
   GID se ukládá pomocí jeho vlastní property string IXmlSerializer.XmlSerialData:

<?xml version="1.0" encoding="utf-16"?>
<persistent Version="1.00">
    <data Value.Type="XmlPersistor.GID" Value="1234;5678" />
</persistent>

   System.Decimal:

<?xml version="1.0" encoding="utf-16"?>
<persistent Version="1.00">
    <data Value.Type="System.Decimal" Value="12.45" />
</persistent>


   Array:

<?xml version="1.0" encoding="utf-16"?>
<persistent Version="1.00">
    <data>
        <Value Value.Type="System.Object[]" Array.Range="[0+7]">
            <Item Item.Indices="[0]" Value.Type="System.Int32" Value="12" />
            <Item Item.Indices="[1]" Value.Type="System.String" Value="Pokusník" />
            <Item Item.Indices="[2]" Value.Type="XmlPersistor.GID" Value="2;500" />
            <Item Item.Indices="[3]" Value.Type="System.DateTime" Value="2011-11-08 10:04 10:04:01" />
            <Item Item.Indices="[4]" Value.Type="System.Drawing.Rectangle" Value="10;50;60;30" />
            <Item Item.Indices="[5]" Value.Type="System.Data.SqlTypes.SqlInt32" Value="60" />
            <Item Item.Indices="[6]" Value.Type="System.Data.SqlTypes.SqlInt32" Value="" />
        </Value>
    </data>
</persistent>

   Hashtable:

<?xml version="1.0" encoding="utf-16"?>
<persistent Version="1.00">
    <data>
        <Value Value.Type="System.Collections.Hashtable">
            <Item Key.Type="XmlPersistor.GID" Key="16;16" Value.Type="System.Drawing.Point" Value="400;300" />
            <Item Key.Type="System.DateTime" Key="2011-11-08 10:04 10:04:23">
                <Value Value.Type="XmlPersistor.DataObject" BindFlags="Default" Count="0" ObjectId="0" InternalDecimal="0.00" NextGId="0;0" Operation="0" PrevGId="0;0" Product_Order_Structure="" Product_Order26="" VstupníStrana="Vychod">
                    <Days>
                        <Day Value="1" />
                        <Day Value="2" />
                        <Day Value="3" />
                    </Days>
                    <ItemDict />
                    <ItemList />
                    <MatrixFloat Array.Range="[0+1,0+1]">
                        <Item Item.Indices="[0,0]" Value="0.00" />
                    </MatrixFloat>
                    <MatrixObj Array.Range="[0+1,0+1]">
                        <Item Item.Indices="[0,0]" />
                    </MatrixObj>
                </Value>
            </Item>
            <Item Key.Type="System.Decimal" Key="26.20" Value.Type="System.Char" Value="a" />
            <Item Key.Type="System.Int32" Key="16" Value.Type="System.String" Value="Pokus" />
        </Value>
    </data>
</persistent>



*/
        #endregion
    }
	#region class XmlPersistSaveArgs : sada parametrů předávaných mezi vnitřními metodami XmlPersist při serializaci objektu
	/// <summary>
	/// XmlPersistSaveArgs : sada parametrů předávaných mezi vnitřními metodami XmlPersist při serializaci objektu
	/// </summary>
    internal class XmlPersistSaveArgs
	{
		internal XmlPersistSaveArgs(object data, string objectName, Type estimatedType, XmlElement xmlElement, TypeLibrary typeLibrary)
		{
			this.Data = data;
			this.ObjectName = objectName;
			this.EstimatedType = estimatedType;
			this.ItemName = null;
			this.XmlElement = xmlElement;
			this.DataTypeInfo = (data == null ? null : typeLibrary.GetInfo(data.GetType()));
		}
        internal XmlPersistSaveArgs(object data, string objectName, Type estimatedType, string itemName, XmlElement xmlElement, TypeLibrary typeLibrary)
		{
			this.Data = data;
			this.ObjectName = objectName;
			this.EstimatedType = estimatedType;
			this.ItemName = itemName;
			this.XmlElement = xmlElement;
			this.DataTypeInfo = (data == null ? null : typeLibrary.GetInfo(data.GetType()));
		}
		internal object Data;
		internal bool HasData { get { return (this.Data != null); } }
		internal string ObjectName;
		internal Type EstimatedType;
		internal string ItemName;
		internal string TypeAttributeName;
		internal XmlElement XmlElement;
		internal Type DataType { get { return (this.DataTypeInfo == null ? null : this.DataTypeInfo.DataType); } }
		internal TypeInfo DataTypeInfo;
		/// <summary>
		/// Vrátí jméno elementu, který obsahuje jednu položku (listu, dictionary, atd)
		/// </summary>
		/// <param name="defaultName"></param>
		/// <returns></returns>
		internal string GetItemName(string defaultName)
		{
			return (String.IsNullOrEmpty(this.ItemName) ? "Item" : this.ItemName);
		}
	}
	#endregion
	#region class XmlPersistLoadArgs : sada parametrů předávaných mezi vnitřními metodami XmlPersist při deserializaci objektu
	/// <summary>
	/// XmlPersistLoadArgs : sada parametrů předávaných mezi vnitřními metodami XmlPersist při deserializaci objektu
	/// </summary>
	internal class XmlPersistLoadArgs
	{
        internal XmlPersistLoadArgs(XmlPersistArgs parameters, PropInfo propInfo, Type dataType, TypeInfo dataTypeInfo, XmElement xmElement, XmAttribute xmAttribute)
        {
            this.Parameters = parameters;
            this.PropInfo = propInfo;
            this.DataType = dataType;
            this.DataTypeInfo = dataTypeInfo;
            this.XmElement = xmElement;
            this.XmAttribute = xmAttribute;
        }
        internal XmlPersistArgs Parameters;
        internal PropInfo PropInfo;
        internal Type DataType;
        internal TypeInfo DataTypeInfo;
        internal XmElement XmElement;
        internal XmAttribute XmAttribute;
	}
	#endregion
	#region enum XmlPersistenceType
	/// <summary>
	/// Typ objektu z hlediska persistence
	/// </summary>
    internal enum XmlPersistenceType
	{
		/// <summary>
		/// Tento druh dat se neukládá
		/// </summary>
		None = 0,
		/// <summary>
		/// Jde o jednoduchou, jednorozměrnou hodnotu, kterou je možno uložit do XML atributu (Name = "value")
		/// </summary>
		Simple,
		/// <summary>
		/// Tato property je typu, který deklaruje, že sám provádí XmlPersistenci (z/do stringu). Do XML se formátuje jako Simple (do atributu).
		/// </summary>
		Self,
        /// <summary>
        /// Enumy
        /// </summary>
        Enum,
		/// <summary>
		/// Složený objekt, budeme hledat sadu jeho property. Pro představu: Jeden záznam (do šířky).
		/// </summary>
		Compound,
        /// <summary>
        /// Standardní pole prvků
        /// </summary>
        Array,
		/// <summary>
		/// IList, seznam, má více položek (řádků), přičemž v jednom řádku je uložen jeden objekt. Typicky List.
		/// </summary>
		IList,
		/// <summary>
		/// IDictionary, má více položek (řádků), přičemž v jednom řádku je uložena hodnota Key+Value.
		/// </summary>
		IDictionary
	}
    /// <summary>
    /// Určení úrovně, v níž se má hledat XML element
    /// </summary>
    internal enum XmlElementDepthType
    {
        None = 0,
        /// <summary>
        /// Kdekoliv (ve zdejší, v nižší i ve vyšší úrovni)
        /// </summary>
        Anywhere,
        /// <summary>
        /// Výhradně ve zdejší úrovni
        /// </summary>
        InCurrentDepth,
        /// <summary>
        /// Ve zdejší úrovni a v kterékoli z podřízených úrovní
        /// </summary>
        CurrentAndAnyChilds,
        /// <summary>
        /// Ve zdejší úrovni a v nejbližší podřízené úrovni
        /// </summary>
        CurrentAndMyChild,
        /// <summary>
        /// Pouze v podřízených úrovních
        /// </summary>
        OnlyInChilds,
		/// <summary>
		/// Pouze přímo podřízené úrovně (current + 1)
		/// </summary>
		OnlyMyOwnChild
    }
    #endregion
}
